﻿using System;
using System.Collections.Generic;
using System.Text;
using Mighty.Common;
using log4net;
using log4net.Config;

namespace Mighty.Brain
{
    /// <summary>
    /// 플레이어 유형
    /// </summary>
    public enum PlayerType { AttackPlayer, FriendPlayer, DependPlayer };

    /// <summary>
    /// 사용여부
    ///  * unknown : 누가 가졌는지 모른다. (bossGuess에서만 사용)
    ///  * none    : 안사용
    ///  * have    : 가지고 있음
    ///  * use     : 해당 라운드에서 사용  (bossData에서만 사용)
    ///  * used    : 이전 라우드에서 사용
    /// </summary>
    public enum UsedType { unknown, none, have, use, used };

    /// <summary>
    /// 카운트할 카드 유형
    ///  * DependPlayerGiruda : 수비가 가진 기루다 개수
    ///  * DependPlayerJoker  : 수비가 가진 조커의 개수 (0 or 1)
    ///  * MyGiruda           : 내 기루다 개수
    ///  * MySpecial          : 마이티와 조커와 같은 특수카드 개수
    ///  * MyHighestCard      : 윈카드 개수
    ///  * RemainDependPlayer : 남아 있는 수비인원 (프랜드가 밝혀지지 않으면 모두 수비로 계산)
    ///  * RoundScore         : 해당 라운드의 그림카드 개수
    /// </summary>
    public enum CountCardType { DependPlayerGiruda, DependPlayerJoker, MyGiruda, MySpecial, MyHighestCard, RemainDependPlayer, RoundScore };

    /// <summary>
    /// 뽑아낼 카드 유형
    ///  * Mighty        : 마이티
    ///  * Joker         : 조커 
    ///  * HighestGiruda : 모든카드중에서 가장 높은 기루다
    ///  * HighGiruda    : 가진카드중에서 가장 높은 기루다
    ///  * LowGiruda     : 가진카드중에서 가장 낮은 기루다
    ///  * HighestCard   : 해당무늬에서 최강 카드, 선일경우 기루다를 제외한 무늬에서 최강 카드
    ///  * HighCard      : 해당무늬에서 높은 카드, 선일경우 기루다를 제외한 무늬에서 높은 카드
    ///  * LowWinCard    : 해당라운드에서 이길 수 있는 가장 낮은 카드
    ///  * Score         : 점수카드, risk가 0인애들은 버릴카드에서 선택되지 않는다
    ///  * LowCard       : 쓸모없는카드
    /// </summary>
    public enum DrawCardType { Mighty, Joker, HighestGiruda, HighGiruda, LowGiruda, HighestCard, HighCard, LowWinCard, Score, LowCard };
 
    /// <summary>
    /// 수집된 게임 정보. 게임전체에 대한 정보. 플레이어별 정보가 필요하다면 guess를 이용한다.
    /// </summary>
    public class BossData
    {
        private GameInfo gameInfo;
        private static readonly ILog log = LogManager.GetLogger(typeof(BossBrain));
        
        /// <summary>
        /// 제일 많은 점수를 획득한 무늬
        ///  * boss의 점수계산법에 의해서 계산된 가장 강력한 무늬
        /// </summary>
        public Shape highShape = Shape.Clover;

        /// <summary>
        /// 최대 공약 가능 점수
        /// </summary>
        public int highScore = -1;

        /// <summary>
        /// 각 무늬별 카드 개수
        /// </summary>
        public int[] cntInitShape = new int[4] { 0, 0, 0, 0 };

        /// <summary>
        /// 초구 프랜드가 필요한지에 대한 여부
        /// </summary>
        public bool needFirstWin;

        /// <summary>
        /// HandedCard에서 카드의 위치
        ///  * 카드를 낼때 사용한다
        /// </summary>
        public int[,] indexCard = new int[4, 15];
        public int indexJoker = -1;

        /// <summary>
        /// 카드의 사용정보
        ///  * 카드의 상태는 have, none 상태에서 use 그리고 used 상태로 변경된다
        /// </summary>
        public UsedType[,] usedCard = new UsedType[4, 15];
        public UsedType usedMighty = UsedType.none;             
        public UsedType usedJoker = UsedType.none;

        /// <summary>
        /// 숫자별로 정렬된 카드
        /// </summary>
        public SortedList<int, HandedCard>[] sortedCard = new SortedList<int, HandedCard>[4];

        /// <summary>
        /// 해당라운드에서 랭킹
        ///  * 모든 카드에 대해서 랭킹을 정한다
        ///  * 마이티, 조커 포함
        /// </summary>
        public int[,] roundRankingCard = new int[4, 15];
        public int roundRankingJoker = 0;

        /// <summary>
        /// 무늬의 카드별 위험도
        /// * 자기가 가지고 있지 않은 카드에 대한 risk
        /// * 동일한 무늬에서 각 카드보다 높은 숫자를 가지는 카드갯수
        /// </summary>
        public int[,] cardRisk = new int[4, 15];        

        /// <summary>
        /// 난 누구?? 
        ///  * 어떠한 역활을 해야 되는지에 대한 정보
        /// </summary>
        public PlayerType playerType;   

        /// <summary>
        /// 프랜드의 위치
        ///  * friend의 위치가 명시적으로 밝혀지지 않더라도 bossGuess를 이용해서 먼저 찾아낼 수 도 있다
        ///  * -1: 찾아내지 못했다
        /// </summary>
        public int friendPosition;      

        /// <summary>
        /// 각 사람별 카드 정보
        ///  * 예측정보임
        /// </summary>
        public BossGuess[] guess = new BossGuess[5];


        public BossData()
        {
        }

        public void InitData(GameInfo g)
        {
            gameInfo = g;
        }

        /// <summary>
        /// 나의 Card 정보를 생성한다. 이전에 저장된 정보를 사용하지 않고 매번 초기화 한다
        /// </summary>
        /// <param name="g"></param>
        public void UpdateMyCard()
        {
            // 초기화
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 15; j++)
                {
                    usedCard[i, j] = UsedType.none;
                    indexCard[i, j] = -1;
                }
                usedMighty = UsedType.none;
                usedJoker = UsedType.none;
                sortedCard[i] = new SortedList<int, HandedCard>();
            }
            for (int i = 0; i < 4; i++) cntInitShape[i] = 0;

            // 내가 가지고 있는 카드 have로 표시, 3장 버려진 카드는 used로 표시
            for (int i = 0; i < gameInfo.myCards.Length; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                UsedType u = UsedType.have;
                if (h.isUsed) u = UsedType.used;
                if (i > 9) u = UsedType.used;

                int shape = (int)h.shape;
                if (h.shape == Shape.Joker)
                {
                    usedJoker = u;
                    indexJoker = i;
                }
                else
                {
                    if (gameInfo.betting.giruda == Giruda.Spade)
                    {
                        if (h.shape == Shape.Diamond && h.number == 14) usedMighty = u;
                    }
                    else
                    {
                        if (h.shape == Shape.Spade && h.number == 14) usedMighty = u;
                    }
                    usedCard[shape, h.number] = u;
                    cntInitShape[shape]++;
                    indexCard[shape, h.number] = i;

                    // 가지고 있다면 해당무늬의 숫자로 추가해둠
                    if (u == UsedType.have) sortedCard[shape][h.number] = h;

                }
            }

            // 각 Round에서 사용된 카드 used 또는 use로 표시
            for (int roundIdx = 0; roundIdx <= gameInfo.roundIdx; roundIdx++)
            {
                UsedType u = UsedType.used;
                if (roundIdx == gameInfo.roundIdx) u = UsedType.use;

                Round round = gameInfo.roundHistory[roundIdx];
                if (round == null) continue;
                for (int cardIdx = 0; cardIdx < 5; cardIdx++)
                {
                    RoundCard roundCard = round.cardStack[cardIdx];
                    if (roundCard == null) continue;

                    int shape = (int)roundCard.shape;
                    if (roundCard.shape == Shape.Joker)
                    {
                        usedJoker = u;
                    }
                    else
                    {
                        if (gameInfo.betting.giruda == Giruda.Spade)
                        {
                            if (roundCard.shape == Shape.Diamond && roundCard.number == 14) usedMighty = u;
                        }
                        else
                        {
                            if (roundCard.shape == Shape.Spade && roundCard.number == 14) usedMighty = u;
                        }
                        usedCard[shape, roundCard.number] = u;
                    }
                }
            }
        }

        /// <summary>
        /// 이번 라운드에서 카드의 랭킹을 업데이트한다
        /// </summary>
        public void UpdateRoundRanking()
        {
            for (int i = 0 ; i < 4 ;i++ )
                for (int j = 14; j > 1; j--)
                    roundRankingCard[i, j] = -1;
            roundRankingJoker = -1;

            int ranking = 0;
            RoundCard firstCard = gameInfo.round.cardStack[0];

            // 마이티
            if (gameInfo.betting.giruda == Giruda.Spade)
            {
                if (roundRankingCard[(int)Shape.Diamond, 14] == -1) roundRankingCard[(int)Shape.Diamond, 14] = ranking++;
            }
            else
            {
                if (roundRankingCard[(int)Shape.Spade, 14] == -1) roundRankingCard[(int)Shape.Spade, 14] = ranking++;
            }

            // 조커
            bool baboJoker = false;
            if (gameInfo.roundIdx == 0 || gameInfo.roundIdx == 9) baboJoker = true;
            if (firstCard != null)
            {
                if (firstCard.IsSame(gameInfo.jokerCall) && firstCard.isJokerCall && gameInfo.roundIdx != 0 ) baboJoker = true;
            }
            if (!baboJoker)
                if (roundRankingJoker == -1) roundRankingJoker = ranking++;

            // 기루다 
            bool[] usedShape = new bool[4] { false, false, false, false };
            if ( gameInfo.betting.giruda != Giruda.NoGiruda ) {
                int shape = (int)gameInfo.betting.giruda;
                usedShape[shape] = true;

                for (int j = 14; j > 1; j--)
                {
                    if (roundRankingCard[shape, j] == -1) roundRankingCard[shape, j] = ranking++;
                }
            }

            // 첫번째무늬
            if (firstCard != null)
            {
                Shape firstShape = Shape.Clover;
                if (firstCard.shape == Shape.Joker) firstShape = firstCard.jokerTarget;
                else firstShape = firstCard.shape;

                if ((int)gameInfo.betting.giruda != (int)firstShape)
                {
                    int shape = (int)firstShape;
                    usedShape[shape] = true;

                    for (int j = 14; j > 1; j--)
                    {
                        if (roundRankingCard[shape, j] == -1) roundRankingCard[shape, j] = ranking++;
                    }
                }
            }

            // 나머지카드
            for (int i = 0; i < 4; i++)
            {
                if (usedShape[i]) continue;
                for (int j = 14; j > 1; j--)
                {
                    if (roundRankingCard[i, j] == -1) roundRankingCard[i, j] = ranking;
                }
            }

            if (baboJoker)
                if (roundRankingJoker == -1) roundRankingJoker = ranking++;
        }


        /// <summary>
        /// 내가 Attack, Friend, Depend인지 찾아 낸다. 
        /// </summary>
        /// <param name="g"></param>
        public void UpdatePlayerType()
        {
            playerType = PlayerType.DependPlayer;

            if (gameInfo.myInfo.position == gameInfo.attackPlayer.position)
            {
                playerType = PlayerType.AttackPlayer;
                return;
            }

            if (gameInfo.friend.founded)
            {
                if (gameInfo.myInfo.position == gameInfo.friend.position)
                {
                    playerType = PlayerType.FriendPlayer;
                    return;
                }
            }
            else
            {
                if (gameInfo.friend.howToSelect == FriendPlayer.HowToSelect.card)
                {
                    Card c = gameInfo.friend.cardCondition;
                    if (c.shape == Shape.Joker)
                    {
                        if (usedJoker == UsedType.have) playerType = PlayerType.FriendPlayer;
                    }
                    else
                    {
                        if (usedCard[(int)c.shape, c.number] == UsedType.have) playerType = PlayerType.FriendPlayer;
                    }
                }
            }

        }

        /// <summary>
        /// 상대방 Card 정보를 생성한다. 이전에 저장된 정보를 사용하지 않고 매번 초기화 한다. 
        /// </summary>
        /// <param name="g"></param>
        public void GuessPlayerCard()
        {
            for (int i = 0; i < 5; i++) guess[i] = new BossGuess();

            for (int i = 0; i < 5; i++)
            {
                for (int j = 0 ; j < 4 ; j++ ) 
                {
                    for (int k = 2 ; k < 15 ; k++ ) {
                        guess[i].usedCard[j,k] = UsedType.unknown;
                        guess[i].probCard[j,k] = 0;
                    }
                }
                guess[i].usedJoker = UsedType.unknown;
                guess[i].probJoker = 0;
            }

            // myCards에 대해서
            for (int j = 0; j < 4; j++)
            {
                for (int k = 2; k < 15; k++)
                {
                    guess[gameInfo.myInfo.position].usedCard[j, k] = UsedType.none;
                }
            }
            guess[gameInfo.myInfo.position].usedJoker = UsedType.none;

            foreach (HandedCard h in gameInfo.myCards)
            {
                for (int position = 0; position < 5; position++)
                {
                    if (position == gameInfo.myInfo.position)
                    {
                        if (h.shape == Shape.Joker)
                        {
                            guess[position].usedJoker = UsedType.have;
                        }
                        else
                        {
                            guess[position].usedCard[(int)h.shape, h.number] = UsedType.have;
                        }
                    }
                    else
                    {
                        if (h.shape == Shape.Joker)
                        {
                            guess[position].usedJoker = UsedType.none;
                        }
                        else
                        {
                            guess[position].usedCard[(int)h.shape, h.number] = UsedType.none;
                        }
                    }
                }
            }

            // 각 Round
            for (int roundIdx = 0 ; roundIdx <= gameInfo.roundIdx ; roundIdx++ ) {
                Round round = gameInfo.roundHistory[roundIdx];

                Shape firstShape = Shape.Clover;
                for (int cardIdx = 0; cardIdx < 5; cardIdx++)
                {
                    RoundCard roundCard = round.cardStack[cardIdx];
                    if (roundCard == null) continue;

                    // 해당카드를 used, none으로 표시
                    for (int position = 0; position < 5; position++)
                    {
                        // 카드의 주인
                        if (position == roundCard.owner)
                        {
                            if (roundCard.shape == Shape.Joker)
                            {
                                guess[position].usedJoker = UsedType.used;
                            }
                            else
                            {
                                guess[position].usedCard[(int)roundCard.shape, roundCard.number] = UsedType.used;
                            }
                        }
                        else
                        {
                            if (roundCard.shape == Shape.Joker)
                            {
                                guess[position].usedJoker = UsedType.none;
                            }
                            else
                            {
                                guess[position].usedCard[(int)roundCard.shape, roundCard.number] = UsedType.none;
                            }
                        }
                    }


                    // 첫번째무늬가 없을 경우 해당무늬를 모두 none으로 표시
                    if (cardIdx == 0)
                    {
                        if (roundCard.shape == Shape.Joker) firstShape = roundCard.jokerTarget;
                        else firstShape = roundCard.shape;
                    }
                    else
                    {
                        // 해당무늬를 안 냈다면
                        if (roundCard.shape != firstShape )
                        {
                            // 마이티와 조커도 아니라면
                            if (!roundCard.IsSame(gameInfo.mighty) && roundCard.shape != Shape.Joker)
                            {
                                // 해당 shape의 모든 unknown카드를 none으로 변경
                                for (int i = 2; i < 15; i++)
                                {
                                    if ( guess[roundCard.owner].usedCard[(int)firstShape, i] == UsedType.unknown ) guess[roundCard.owner].usedCard[(int)firstShape, i] = UsedType.none;
                                }

                            }
                        }
                    }
                }
            }


            // 추가예측
            bool pass;
            do
            {
                pass = true;
                for (int i = 0; i < 5; i++) guess[i].UpdateCnt();
                if (GuessPlayerCard1())
                {
                    pass = false;
                    continue;
                }
                if (GuessPlayerCard2())
                {
                    pass = false;
                    continue;
                }
                if (GuessPlayerCard3())
                {
                    pass = false;
                    continue;
                }
                if (GuessPlayerCard4())
                {
                    pass = false;
                    continue;
                }
            } while (!pass);


            // shape확률
            for (int j = 0; j < 4; j++)
            {
                for (int k = 2; k < 15; k++)
                {
                    int cntUnknown = 0;
                    for (int i = 0; i < 5; i++)
                    {
                        if (guess[i].usedCard[j,k] == UsedType.unknown) cntUnknown++;
                    }

                    float prob = 0;
                    if ( cntUnknown != 0 ) prob = (float)1 / cntUnknown;

                    for (int i = 0; i < 5; i++)
                    {
                        if (guess[i].usedCard[j, k] == UsedType.unknown) guess[i].probCard[j, k] = prob;
                        else if (guess[i].usedCard[j, k] == UsedType.none) guess[i].probCard[j, k] = 0;
                        else if (guess[i].usedCard[j, k] == UsedType.have) guess[i].probCard[j, k] = 1;
                        else if (guess[i].usedCard[j, k] == UsedType.use) guess[i].probCard[j, k] = 0;
                        else if (guess[i].usedCard[j, k] == UsedType.used) guess[i].probCard[j, k] = 0;
                    }

                    
                }
            }

            // 무늬기대값 = playerShapeHave + unknown에 대한 해당 비중치(playerShapeProb / playerProb) * (10-playerHave-playerUsed)
            for (int i = 0; i < 5; i++)
            {
                
                float playerProb = 0;
                int playerHave = 0;
                int playerUsed = 0;
                for (int j = 0; j < 4; j++)
                {
                    // 플레이어.have, 플레이어.used
                    playerHave += guess[i].cntShapeHave[j];
                    playerUsed += guess[i].cntShapeUsed[j];

                    for (int k = 2; k < 15; k++)
                    {
                        // 전체무늬확률총합
                        if (guess[i].usedCard[j, k] == UsedType.unknown) playerProb += guess[i].probCard[j, k];

                    }
                }

                for (int j = 0; j < 4; j++)
                {
                    float playerShapeProb = 0;
                    int playerShapeHave = 0;
                    for (int k = 2; k < 15; k++)
                    {
                        // 확률총합, have
                        if (guess[i].usedCard[j, k] == UsedType.unknown) playerShapeProb += guess[i].probCard[j, k];
                        else if (guess[i].usedCard[j, k] == UsedType.have) playerShapeHave++;
                    }
                    
                    float ratio = 0;
                    float ext = 0;
                    if (playerProb!=0) ratio = playerShapeProb / playerProb;
                    ext = (float)playerShapeHave + ratio * (10 - playerHave - playerUsed);
                    guess[i].extShape[j] = ext;
                }
            }

        }


        /// <summary>
        /// player의 10장의 카드를 모두 알고 있다면 unknown->none
        /// </summary>
        /// <returns>update발생여부</returns>
        private bool GuessPlayerCard1()
        {
            bool update = false;
            for (int i = 0; i < 5; i++)
            {
                if (guess[i].cntHave + guess[i].cntUsed == 10 && guess[i].cntUnknown > 0)
                {
                    update = true;
                    for (int j = 0; j < 4; j++)
                    {
                        for (int k = 2; k < 15; k++)
                        {
                            if (guess[i].usedCard[j, k] == UsedType.unknown) guess[i].usedCard[j, k] = UsedType.none;
                        }
                    }
                    if (guess[i].usedJoker == UsedType.unknown) guess[i].usedJoker = UsedType.none;
                }
            }
            return update;
        }

        /// <summary>
        /// shape의 13-used-have=unknown 이라면 unknown->have
        ///  * 주공만이 할 수 있다(나머지 3장의 카드를 알고 있기 때문에)
        /// </summary>
        /// <returns>update발생여부</returns>
        private bool GuessPlayerCard2()
        {
            bool update = false;
            if (playerType != PlayerType.AttackPlayer) return update;
            for (int j = 0; j < 4; j++)
            {
                int cntRemain = 13;
                int cntUnknown = 0;
                for (int i = 0; i < 5; i++)
                {
                    cntRemain -= guess[i].cntShapeUsed[j];
                    cntRemain -= guess[i].cntShapeHave[j];
                    cntUnknown += guess[i].cntShapeUnknown[j];
                }

                if (cntRemain == cntUnknown && cntUnknown > 0)
                {
                    update = true;
                    for (int i = 0; i < 5; i++)
                    {
                        for (int k = 2; k < 15; k++)
                        {
                            // none은 특별이 할 필요 없다
                            if (guess[i].usedCard[j, k] == UsedType.unknown) guess[i].usedCard[j, k] = UsedType.have;
                        }
                    }
                }
            }
            return update;
        }

        /// <summary>
        /// player의 10-used-have=unknown 이라면 unknown->have (주공의 경우 10-used-have의 경우 항상 -3을 유지)
        /// </summary>
        /// <returns>update발생여부</returns>
        private bool GuessPlayerCard3()
        {
            bool update = false;
            for (int i = 0; i < 5; i++)
            {
                int cntRemain = 10;
                int cntUnknown = 0;
                for (int j = 0; j < 4; j++)
                {
                    cntRemain -= guess[i].cntShapeUsed[j];
                    cntRemain -= guess[i].cntShapeHave[j];
                    cntUnknown += guess[i].cntShapeUnknown[j];
                }

                if (cntRemain == cntUnknown && cntUnknown > 0)
                {
                    update = true;
                    for (int j = 0; j < 4; j++)
                    {
                        for (int k = 2; k < 15; k++)
                        {
                            if (guess[i].usedCard[j, k] == UsedType.unknown)
                            {
                                for (int l = 0; l < 5; l++)
                                {
                                    if (i == l) guess[l].usedCard[j, k] = UsedType.have;
                                    else guess[l].usedCard[j, k] = UsedType.none;
                                }

                            }
                        }
                    }
                }
            }
            return update;
        }

       
        /// <summary>
        /// card의 none=4,unknown=1 이라면 unknown->have
        ///  * 주공만이 할 수 있다(나머지 3장의 카드를 알고 있기 때문에)
        /// </summary>
        /// <returns>update발생여부</returns>
        private bool GuessPlayerCard4()
        {
            bool update = false;
            if (playerType != PlayerType.AttackPlayer) return update;
            for (int j = 0; j < 4; j++)
            {
                for (int k = 2; k < 15; k++)
                {
                    int cntNone = 0;
                    int posUnknown = -1;
                    for (int i = 0; i < 5; i++)
                    {
                        if (guess[i].usedCard[j, k] == UsedType.none) cntNone++;
                        else if (guess[i].usedCard[j, k] == UsedType.unknown) posUnknown = i;
                    }

                    if (cntNone == 4 && posUnknown != -1)
                    {
                        update = true;
                        guess[posUnknown].usedCard[j, k] = UsedType.have;
                    }
                }
            }
            return update;
        }

        /// <summary>
        /// Friend player를 추측해 낸다
        /// </summary>
        public void GuessPlayerType()
        {
            friendPosition = -1;
            for (int i = 0; i < 5; i++) guess[i].playerType = PlayerType.DependPlayer;
            guess[gameInfo.attackPlayer.position].playerType = PlayerType.AttackPlayer;
            if (gameInfo.friend.founded)
            {
                guess[gameInfo.friend.position].playerType = PlayerType.FriendPlayer;
                friendPosition = gameInfo.friend.position;
            }
            else
            {
                if ( gameInfo.friend.howToSelect == FriendPlayer.HowToSelect.card ) {
                    int shape = (int)gameInfo.friend.cardCondition.shape;
                    for (int i = 0; i < 5; i++)
                    {
                        bool found = false;
                        if (gameInfo.friend.cardCondition.shape != Shape.Joker && guess[i].usedCard[shape, gameInfo.friend.cardCondition.number] == UsedType.have) found = true;
                        else if (gameInfo.friend.cardCondition.shape == Shape.Joker && guess[i].usedJoker == UsedType.have) found = true;

                        if (found)
                        {
                            guess[i].playerType = PlayerType.FriendPlayer;
                            friendPosition = i;
                            break;
                        }
                    }
                }
            }
        }

        public int GetRoundRanking(Card c)
        {
            return GetRoundRanking(c.shape, c.number);
        }

        public int GetRoundRanking(Shape s, int number)
        {
            if (s == Shape.Joker) return roundRankingJoker;
            else return roundRankingCard[(int)s, number];
        }


        /// <summary>
        /// type에 해당되는 card개수를 센다
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public int CountCard(CountCardType type)
        {
            int cnt = 0;
            if (type == CountCardType.DependPlayerGiruda && gameInfo.betting.giruda != Giruda.NoGiruda )
            {
                int shape = (int)gameInfo.betting.giruda;
                for ( int i = 2 ; i < 15 ; i++ ) {
                    bool depend = true;
                    if (usedCard[shape, i] == UsedType.have || usedCard[shape, i] == UsedType.use || usedCard[shape, i] == UsedType.used) depend = false;
                    if ( friendPosition != -1 && guess[friendPosition].usedCard[shape, i] == UsedType.have) depend = false;
                    if (depend) cnt++;
                }
            }
            else if (type == CountCardType.DependPlayerJoker)
            {
                bool depend = true;
                if ( usedJoker == UsedType.have || usedJoker == UsedType.use || usedJoker == UsedType.used ) depend = false;
                if ( friendPosition != -1 && guess[friendPosition].usedJoker == UsedType.have ) depend = false;

                if (depend) cnt++;
            }
            else if (type == CountCardType.MyGiruda)
            {
                int shape = (int)gameInfo.betting.giruda;
                cnt = guess[gameInfo.myInfo.position].cntShapeHave[shape];
            }
            else if (type == CountCardType.MySpecial)
            {
                if ( usedMighty == UsedType.have ) cnt++;
                if ( usedJoker == UsedType.have ) cnt++;
            }
            else if (type == CountCardType.MyHighestCard)
            {
                // 기루다와 특수카드를 제외한 최강카드개수
                int mightyShape = (int)Shape.Spade;
                if ( gameInfo.betting.giruda == Giruda.Spade ) mightyShape = (int)Shape.Diamond;
                for (int i = 0; i < 4; i++)
                {
                    if (i == (int)gameInfo.betting.giruda) continue;
                    for (int j = 14; j > 1; j--)
                    {
                        if (usedCard[i, j] == UsedType.have) cnt++;
                        else if (usedCard[i, j] == UsedType.none && !(i == mightyShape && j == 14)) break;
                    }
                }
            }
            else if (type == CountCardType.RemainDependPlayer)
            {
                int pos = gameInfo.myInfo.position;
                for (int i = gameInfo.roundCardIdx + 1 ; i < 5; i++)
                {
                    pos++;
                    if (pos == 5) pos = 0;
                    if (guess[pos].playerType == PlayerType.DependPlayer) cnt++;
                }
            }
            else if (type == CountCardType.RoundScore)
            {
                // 해당라운드의 그림개수
                for (int i = 0; i < gameInfo.roundCardIdx; i++)
                {
                    if (gameInfo.round.cardStack[i].number >= 10) cnt++;
                }
            }
            return cnt;
        }

        /// <summary>
        /// type에 해당되는 카드를 뽑아낸다. 
        ///  * 몇몇카드를 제외하고 현재 라운드에서 이길 만해야 return한다
        ///  * 가지고 있지 않다면 null을 return함
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public HandedCard DrawCard(DrawCardType type)
        {
            HandedCard h = null;

            if (log.IsDebugEnabled)
            {
                int lineNumber = new System.Diagnostics.StackTrace(true).GetFrame(1).GetFileLineNumber();
                log.Debug(String.Format("\t{0} {1}\t{2} ({3})", gameInfo.roundIdx, gameInfo.myInfo.position, type.ToString(), lineNumber));
            }

            Shape firstShape = Shape.Clover;;
            if (gameInfo.roundCardIdx != 0)
            {
                RoundCard firstCard = gameInfo.round.cardStack[0];
                if (firstCard.shape == Shape.Joker) firstShape = firstCard.jokerTarget;
                else firstShape = firstCard.shape;
            }

            if (type == DrawCardType.Mighty)
            {
                Shape s = Shape.Spade;
                if (gameInfo.betting.giruda == Giruda.Spade) s = Shape.Diamond;
                if (usedMighty == UsedType.have) return DrawCard(s, 14);
            }
            else if (type == DrawCardType.Joker)
            {
                if (usedJoker == UsedType.have && Winable(Shape.Joker, 0)) return DrawCard(Shape.Joker, 0);
            }
            else if (type == DrawCardType.HighestGiruda && gameInfo.betting.giruda != Giruda.NoGiruda)
            {
                int s = (int)gameInfo.betting.giruda;
                for (int i = 14; i > 1; i--)
                {
                    if (usedCard[s, i] == UsedType.have && Drawable((Shape)s, i) && Winable((Shape)s, i)) return DrawCard((Shape)s, i);
                    else if (usedCard[s, i] == UsedType.none) break;
                }
            }
            else if (type == DrawCardType.HighGiruda && gameInfo.betting.giruda != Giruda.NoGiruda)
            {
                int s = (int)gameInfo.betting.giruda;
                for (int i = 14; i > 1; i--)
                {
                    if (usedCard[s, i] == UsedType.have && Drawable((Shape)s, i) && Winable((Shape)s, i)) return DrawCard((Shape)s, i);
                }
            }
            else if (type == DrawCardType.LowGiruda && gameInfo.betting.giruda != Giruda.NoGiruda)
            {
                int s = (int)gameInfo.betting.giruda;
                for (int i = 2 ; i < 15 ; i++)
                {
                    if (usedCard[s, i] == UsedType.have && Drawable((Shape)s, i) && Winable((Shape)s, i)) return DrawCard((Shape)s, i);
                }
            }
            else if (type == DrawCardType.HighestCard)
            {
                // 해당무늬에서 최강 카드, 선일경우 기루다를 제외한 무늬에서 최강 카드
                if (gameInfo.roundCardIdx == 0)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        if (gameInfo.betting.giruda != Giruda.NoGiruda && (int)gameInfo.betting.giruda == i) continue;
                        for (int j = 14; j > 1; j--)
                        {
                            if (gameInfo.betting.giruda == Giruda.Spade && i == (int)Shape.Diamond && j == 14) continue;
                            else if (gameInfo.betting.giruda != Giruda.Spade && i == (int)Shape.Spade && j == 14) continue;

                            if (usedCard[i, j] == UsedType.have && Drawable((Shape)i, j) && Winable((Shape)i, j)) return DrawCard((Shape)i, j);
                            else if (usedCard[i, j] == UsedType.none) break;
                        }
                    }
                }
                else
                {
                    for (int j = 14; j > 1; j--)
                    {
                        if (gameInfo.betting.giruda == Giruda.Spade && firstShape == Shape.Diamond && j == 14) continue;
                        else if (gameInfo.betting.giruda != Giruda.Spade && firstShape == Shape.Spade && j == 14) continue;

                        if (usedCard[(int)firstShape, j] == UsedType.have && Drawable(firstShape, j) && Winable(firstShape, j)) return DrawCard(firstShape, j);
                        else if (usedCard[(int)firstShape, j] == UsedType.none || usedCard[(int)firstShape, j] == UsedType.use) break;  // 현재 라운드에서 사용한 카드도 포함해서
                    }
                }

            }
            else if (type == DrawCardType.HighCard)
            {
                if (gameInfo.roundCardIdx == 0)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        if (gameInfo.betting.giruda != Giruda.NoGiruda && (int)gameInfo.betting.giruda == i) continue;
                        for (int j = 14; j > 1; j--)
                        {
                            if (gameInfo.betting.giruda == Giruda.Spade && i == (int)Shape.Diamond && j == 14) continue;
                            else if (gameInfo.betting.giruda != Giruda.Spade && i == (int)Shape.Spade && j == 14) continue;

                            if (usedCard[i, j] == UsedType.have && Drawable((Shape)i, j) && Winable((Shape)i, j)) return DrawCard((Shape)i, j);
                        }
                    }
                }
                else
                {
                    for (int j = 14; j > 1; j--)
                    {
                        if (gameInfo.betting.giruda == Giruda.Spade && firstShape == Shape.Diamond && j == 14) continue;
                        else if (gameInfo.betting.giruda != Giruda.Spade && firstShape == Shape.Spade && j == 14) continue;

                        if (usedCard[(int)firstShape, j] == UsedType.have && Drawable(firstShape, j) && Winable(firstShape, j)) return DrawCard(firstShape, j);
                    }
                }
            }
            else if (type == DrawCardType.LowWinCard && gameInfo.roundCardIdx != 0)
            {
                for (int j = 2; j < 15; j++)
                {
                    if (gameInfo.betting.giruda == Giruda.Spade && firstShape == Shape.Diamond && j == 14) continue;
                    else if (gameInfo.betting.giruda != Giruda.Spade && firstShape == Shape.Spade && j == 14) continue;

                    if (usedCard[(int)firstShape, j] == UsedType.have && Drawable(firstShape, j) && Winable(firstShape, j)) return DrawCard(firstShape, j);
                }
            }
            else if (type == DrawCardType.Score)
            {
                for (int i = 0; i < 4; i++)
                {
                    if ( gameInfo.betting.giruda != Giruda.NoGiruda && (int)gameInfo.betting.giruda == i ) continue; 
                    for (int j = 10; j < 13; j++)
                    {
                        if (gameInfo.betting.giruda == Giruda.Spade && i == (int)Shape.Diamond && j == 14) continue;
                        else if (gameInfo.betting.giruda != Giruda.Spade && i == (int)Shape.Spade && j == 14) continue;

                        if (usedCard[i, j] == UsedType.have && Drawable((Shape)i, j) && cardRisk[i,j] > 1 ) return DrawCard((Shape)i, j);
                    }
                }
            }
            else if (type == DrawCardType.LowCard) 
            {
                Card lowShape = null, lowGiruda = null;
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 2; j < 15; j++)
                    {
                        if (usedCard[i, j] == UsedType.have && Drawable((Shape)i, j))
                        {
                            if (gameInfo.betting.giruda == Giruda.Spade && i == (int)Shape.Diamond && j == 14) continue;
                            else if (gameInfo.betting.giruda != Giruda.Spade && i == (int)Shape.Spade && j == 14) continue;

                            if (gameInfo.betting.giruda != Giruda.NoGiruda && (int)gameInfo.betting.giruda == i) lowGiruda = new Card((Shape)i, j);
                            else lowShape = new Card((Shape)i, j);
                            break;
                        } 
                    }
                }
                if (lowShape != null) return DrawCard(lowShape.shape, lowShape.number);
                if (lowGiruda != null) return DrawCard(lowGiruda.shape, lowGiruda.number);
            }
            return h;
        }

        /// <summary>
        /// 이번 라운드에서 나올수 있는 가장 높은 카드 
        ///  * 첫번째 무늬에 해당되는 가장 높은 카드
        ///  * 이전라운드 까지의 정보만 이용
        ///  * 마이티 제외
        /// </summary>
        /// <returns></returns>
        public Card FindRoundHighestCard()
        {
            if ( gameInfo.roundCardIdx == 0 ) return null;

            RoundCard firstCard = gameInfo.round.cardStack[0];
            Shape firstShape = Shape.Clover;
            if (firstCard.shape == Shape.Joker) firstShape = firstCard.jokerTarget;
            else firstShape = firstCard.shape;

            for (int i = 14; i > 1; i--)
            {
                if (gameInfo.mighty.shape == firstShape && i == 14) continue;
                if (usedCard[(int)firstShape, i] != UsedType.used) return new Card(firstShape, i);
            }
            return null;
        }


        /// <summary>
        /// 조건을 만족하는 카드가 해당 라운드에서 이길만한 카드인지 체크한다
        /// </summary>
        /// <param name="s"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        public bool Winable(Shape s, int number)
        {
            int highestRanking = 99;
            for (int i = 0; i < gameInfo.roundCardIdx; i++)
            {
                int ranking = GetRoundRanking(gameInfo.round.cardStack[i]);
                if (ranking < highestRanking) highestRanking = ranking;
            }
            return (GetRoundRanking(s, number) < highestRanking);
        }

        public bool Winable(Card c)
        {
            return Winable(c.shape, c.number);
        }

        /// <summary>
        /// 조건을 만족하는 카드를 낼 수 있는지 체크한다
        /// </summary>
        /// <param name="s"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        public bool Drawable(Shape s, int number)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (h.shape == s && h.number == number) return h.isPossible;
                else if (h.shape == s && h.shape == Shape.Joker) return h.isPossible;
            }
            return false;
        }

        /// <summary>
        /// 조건을 만족하는 카드를 낸다
        /// </summary>
        /// <param name="s"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        public HandedCard DrawCard(Shape s, int number)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = gameInfo.myCards[i];
                if (h.shape == s && h.shape == Shape.Joker) 
                {
                    if (playerType == PlayerType.AttackPlayer) h.cmdJokerTarget = (Shape)gameInfo.betting.giruda;
                    else if (playerType == PlayerType.FriendPlayer) h.cmdJokerTarget = highShape;
                    return h;
                }
                else if (h.shape == s && h.number == number)
                {
                    return h;
                }
            }
            return null;
        }
        
        /// <summary>
        /// 각 무늬에서 card의 risk를 계산한다. 
        /// </summary>
        public void UpdateCardRisk()
        {
            int risk;
            for ( int i = 0 ; i < 4 ; i++ ) {
                risk = 0;
                for ( int j = 0 ; j < 15 ; j++ ) {
                    cardRisk[i, j] = -1;
                    if (usedCard[i, j] == UsedType.none) cardRisk[i, j] = risk++;
                }
            }
        }
    }
}
